﻿using System;
using System.Collections.Generic;
using System.Text;
using ttbit.math;
using Rect = System.Drawing.Rectangle;
using RectF = System.Drawing.RectangleF;
namespace ttbit.core
{
    public interface IPacker
    {
        int supportSpriteMaxWidth { get; }
        int supportSpriteMinWidth { get; }
        int supportSpriteMaxHeight { get; }
        int supportSpriteMinHeight { get; }

        bool GetFree(ushort width, ushort height, out Rect rect);
        public Rect[] GetFrees(Size[] size);
        public float UsedPercent
        {
            get;
        }
    }

    public class TexturePacker
    {
        IPacker packer;
        public Texture texture
        {
            get;
            private set;
        }
        public TexturePacker(IPacker packer, ushort width, ushort height, bool gray = false)
        {
            this.packer = packer;
            this.texture = new Texture(width, height, gray ? IGL.TextureType.R_U8 : IGL.TextureType.RGBA_32);
        }
        public bool Gray
        {
            get
            {
                return texture.gray;
            }
        }
        public bool AddSprite(SpriteData data, bool withcut, out Sprite sp)
        {
            return AddSprite(data.Width, data.Height, data.Data, data.IsGray, withcut, out sp);
        }
        public bool AddSprite(ushort width, ushort height, byte[] data, bool gray, bool withcut, out Sprite sp)
        {
            if (width < packer.supportSpriteMinWidth
                || width > packer.supportSpriteMaxHeight
                || height < packer.supportSpriteMinHeight
                || height > packer.supportSpriteMaxWidth
                )
                throw new Exception("error size");
            if (gray != texture.gray)
                throw new Exception("error gray state.");
            //加入cut逻辑，自动切补，不能太大，也不能太小
            bool needcut = false;
            int pixeloffsetX = int.MaxValue;
            int pixeloffsetY = int.MaxValue;
            int pixeloffsetXMax = int.MinValue;
            int pixeloffsetYMax = int.MinValue;
            int bytelen = gray ? 1 : 4;

            if (withcut)
            {





                bool empty = true;

                for (var y = 0; y < height; y++)
                {
                    for (var x = 0; x < width; x++)
                    {
                        for (var b = 0; b < bytelen; b++)
                        {
                            if (data[(width * y + x) * bytelen + b] > 0)
                            {
                                if (pixeloffsetX > x)
                                    pixeloffsetX = x;

                                if (pixeloffsetY > y)
                                    pixeloffsetY = y;

                                if (pixeloffsetXMax < x) pixeloffsetXMax = x;
                                if (pixeloffsetYMax < y) pixeloffsetYMax = y;
                                empty = false;
                            }
                        }
                    }
                }
                if (empty)//空的别来凑热闹
                {
                    sp = default(Sprite);
                    return false;
                }

                if (pixeloffsetX != 0 || pixeloffsetY != 0 || pixeloffsetXMax + 1 != width || pixeloffsetYMax + 1 != height || width < 8 || height < 8 || width % 4 > 0 || height % 4 > 0)
                {
                    needcut = true;
                }
            }

            if (needcut)
            {

                ushort uvwidth = (ushort)(pixeloffsetXMax + 1 - pixeloffsetX);
                ushort uvheigth = (ushort)(pixeloffsetYMax + 1 - pixeloffsetY);
                ushort datawidth = (ushort)(uvwidth % 4 > 0 ? (uvwidth / 4 + 1) * 4 : uvwidth);
                ushort dataheight = (ushort)(uvheigth % 4 > 0 ? (uvheigth / 4 + 1) * 4 : uvheigth);

                byte[] newdata = new byte[datawidth * dataheight * bytelen];

                for (int y = 0; y < uvheigth; y++)
                {
                    for (int x = 0; x < uvwidth; x++)
                    {
                        for (var b = 0; b < bytelen; b++)
                        {
                            newdata[(y * datawidth + x) * bytelen + b] = data[((y + pixeloffsetY) * width + x + pixeloffsetX) * bytelen + b];
                        }
                    }
                }
                var br = packer.GetFree(uvwidth, uvheigth, out Rect rect);
                if (!br)
                {
                    sp = default(Sprite);
                    return false;
                }

                var s = new Sprite();
                s.root = texture;
                s.umin = (ushort)rect.X;
                s.umax = (ushort)rect.Right;
                s.vmin = (ushort)rect.Y;
                s.vmax = (ushort)rect.Bottom;
                s.borderX = (byte)pixeloffsetX;
                s.borderY = (byte)pixeloffsetY;
                s.Width = width;
                s.Height = height;
                sp = s;

                texture.UploadSubData((ushort)rect.X, (ushort)rect.Y, (ushort)datawidth, (ushort)dataheight, gray ? IGL.TextureType.R_U8 : IGL.TextureType.RGBA_32, newdata);
                return true;

            }
            else
            {
                var br = packer.GetFree(width, height, out Rect rect);
                if (!br)
                {
                    sp = default(Sprite);
                    return false;
                }

                var s = new Sprite();
                s.root = texture;
                s.umin = (ushort)rect.X;
                s.umax = (ushort)rect.Right;
                s.vmin = (ushort)rect.Y;
                s.vmax = (ushort)rect.Bottom;
                s.borderX = (byte)0;
                s.borderY = (byte)0;
                s.Width = width;
                s.Height = height;
                sp = s;

                texture.UploadSubData((ushort)rect.X, (ushort)rect.Y, (ushort)width, (ushort)height, gray ? IGL.TextureType.R_U8 : IGL.TextureType.RGBA_32, data);
                return true;

            }

        }

    }
}
